import { DistStruct } from "../../common/src/CreateDist";
import { RunComponent } from "../../common/src/RunComponent";
import { Decode } from "./Decode";
import less from "less";

export class ImplementComponent {
  private runtime: RunComponent;
  // private js: string[];
  // private css: string[];
  private dom: cheerio.Cheerio;
  // private dist: DistStruct;
  private $: cheerio.Root;
  // private componentMark: string;
  // private indexFilePath: string;
  private decoder: Decode;

  private cssFileArr: string[];
  private jsFileArr: string[];
  constructor(
    runtime: RunComponent,
    distStructure: DistStruct,
    decoder: Decode,
    $: cheerio.Root,
    cssFileArr: string[],
    jsFileArr: string[]
  ) {
    this.runtime = runtime;
    // this.dist = distStructure;
    this.decoder = decoder;
    this.$ = $;
    this.cssFileArr = cssFileArr;
    this.jsFileArr = jsFileArr;
    // this.componentMark = runtime.getModuleId();
    // this.js = runtime.getJs();
    // this.css = runtime.getCss();
    this.dom = runtime.getDom();

    // const loaded = this.appendHTML();
    this.preLoad();
    this.appendHTML()
    this.loaded();
    // loaded();
    // this.appendCss();
    // this.appendJs();
  }

  private appendHTML() {
    const frag = this.dom.clone();
    frag.attr("id", this.runtime.getModuleId());
    // const task: (() => void)[] = [];
    // console.log(this.decoder.getAction(), 'action');

    this.decoder.getAction().forEach((act) => {
      const originFun = new Function(`return ${act}`)

      originFun()(this.$, frag);

      // task.push(() => {
      //   originFun(frag);
      // });
    });

    this.$("body").append(frag);
    // console.log(this.$.html());

    // return () => {
    //   task.forEach((val) => val());
    // };
  }

  public async appendCss() {
    let styles = this.decoder.getStyle()[this.getStyleTagMark()];

    // console.log(this.decoder.getStyle(), this.getStyleTagMark());

    // if styles does not exist using old style from runtime
    if (!styles) styles = this.runtime.getCss().join("\n");

    this.cssFileArr.push(await this.cssPreprocess(styles));
  }

  public appendJs() {
    const js = this.runtime.getJs().join("\n");
    this.jsFileArr.push(this.jsPrepossessor(js));
  }

  private preLoad() {
    const mod = this.runtime.getModule();
    // console.log(
    //   this.$("#" + this.runtime.getModuleId()).find("img").length,
    //   this.runtime.getResourceList(),
    //   this.runtime.getModuleId(),
    //   "preload"
    // );

    mod.beforeLoad &&
      mod.beforeLoad(
        this.$,
        this.$("#" + this.runtime.getModuleId()),
        this.runtime.getResourceList(),
        this.runtime.getModuleId()
      );
  }

  private loaded() {
    const mod = this.runtime.getModule();
    // console.log(
    //   this.$("#" + this.runtime.getModuleId()).find("img").length,
    //   this.runtime.getResourceList(),
    //   this.runtime.getModuleId(),
    //   "preload"
    // );

    mod.afterLoad &&
      mod.afterLoad(
        this.$,
        this.$("#" + this.runtime.getModuleId()),
        this.runtime.getResourceList(),
        this.runtime.getModuleId()
      );
  }

  /**
   * scope js with module name exposed!
   * @param js string of all js file
   * @returns scoped js file
   */
  private jsPrepossessor(js: string) {
    return `((moduleName)=>{\n${js}\n})('${this.runtime.getModuleId()}');`;
  }

  private cssPreprocess(css: string): Promise<string> {
    //use less convert css
    return new Promise((resolve, reject) => {
      less
        .render(css, {
          compress: true,
        })
        .then(
          function (output) {
            resolve(output.css);
          },
          function (error) { }
        );
    });
  }
  private getStyleTagMark() {
    // console.log(this.runtime.getModuleId(), "moduleId from get style tag mark");

    return "#style_" + this.runtime.getModuleId();
  }
  private getScriptTagMark() {
    return "#script_" + this.runtime.getModuleId();
  }
}
